﻿using AnalyStock.DataManage;
using static AnalyStock.DataAnalysis.FillDataCollection;
using static AnalyStock.DataManage.QueryCandlePointsExtension;

namespace AnalyStock.DataAnalysis;

public static class SeleStockTactics
{
    public static async Task DoWorkOnTacticAsync(int indexTactic, StockInfo currentStock, CancellationToken token)
    {
        token.ThrowIfCancellationRequested();
        var isInitializeDatas = await IsFilltoBasicDataAsync(currentStock.Ts_code,
                                                             LoadTactics()[indexTactic].Param)
                                                            .ConfigureAwait(false);
        if (isInitializeDatas)
        {
            LoadTactics()[indexTactic].DoWork_Action?.Invoke(currentStock,
                                                             LoadTactics()[indexTactic].Param);
        }
    }

    public static string[] GetTacticNames()
    {
        return LoadTactics().Select(n => n.Description).ToArray();
    }

    private static DoworkOfTactics[] LoadTactics()
    {
        return new DoworkOfTactics[7]
        {
            new("1.上升走势/接近近期最高拐点95-105%",
                SeleNeartoLastTopPoint,
                new(CandlePointSet.PeriodType.Daily,6,0,false)),
            new("2.回调走势/120日内振幅50%/底部振荡/价格在近期高点75%-115%",
                SeleBottomWaveInLastDays,
               new(CandlePointSet.PeriodType.Daily,6,120,false)),
            new("3.✦涨停回调不破5日均线",
                SeleSkyrocketingtoBack,
                new(CandlePointSet.PeriodType.Daily,6,5,false)),
            new("4.✦涨停回调15-30%之间",
                SeleSkyrocketingtoBack30P,
                new(CandlePointSet.PeriodType.Daily,6,0,false)),
            new("5.✦24个月/价格连续下降/量能持续萎缩/出现5月线金叉",
                SeleSteadyDecreaseIn24Months,
                new(CandlePointSet.PeriodType.Monthly,20,0,false)),
            new("6.✦尾盘30分钟异动股",
                SeleChangeOn30MinutesOfLateDay,
                new(CandlePointSet.PeriodType.Daily,6,0,false)),
            new("7.✦早盘30分钟冲击涨停股",
                SeleSkyrocketingOnEarlyMarket,
                new(CandlePointSet.PeriodType.Daily,6,0,true)),
        };
    }

    /// <summary>
    ///     1 股价创新高
    /// </summary>
    /// <param name="index"></param>
    private static void SeleNeartoLastTopPoint(StockInfo currentStock, CandlePointsParam param)
    {
        //1.选择趋势向上
        var (trendLable, trendDescribe)
            = CandlePoints.CreateNorderFittingPointsBySample(3).FittingPoints.AnalyTrendOfKline();
        if (!trendLable.Contains('F') || string.IsNullOrEmpty(trendDescribe))
        {
            return;
        }

        //2.选择当前价格在近期高点的95%-105%之间
        var (isTop, lastTop) = SamplePoints.GetLastTopPoint();
        if (!isTop || lastTop.GrowToCurrent is < 0.95f or > 1.05f)
        {
            return;
        }

        var (isFina, finaIndex) = FinancialIndexs.GetLastFinaIndex();
        SeleStockKeyIndicators.Add(new StockKeyIndicators
        {
            StockCode = currentStock.Ts_code,
            StockName = currentStock.Name,
            CurrentClose = SamplePoints.Last().Close,
            LastEps = isFina ? finaIndex.Eps : 0,
            FloatMv = currentStock.Float_mv,
            DaysToMaxCLose = lastTop.DaysToCurrent,
            GrowRate = $"{lastTop.GrowToCurrent * 100.0f:F2}%",
            Industry = currentStock.Industry,
            Trend = trendDescribe,
            Description = $"股价超过{lastTop.DaysToCurrent}日前拐点"
                          + $"增长了{lastTop.GrowToCurrent * 100.0f:0.00}%"
        });
    }

    /// <summary>
    ///     冲高走势的股票，days日内倍量冲高，回调不破位
    /// </summary>
    /// <param name="index"></param>
    /// <param name="days"></param>
    private static void SeleUptoDoubleVolOnUpward(StockInfo currentStock, CandlePointsParam param)
    {
        //1、趋势开始向上
        var (trendLable, trendDescribe)
            = CandlePoints.CreateNorderFittingPointsBySample(3).FittingPoints.AnalyTrendOfKline();
        if (!trendLable.Contains('F') || string.IsNullOrEmpty(trendDescribe))
        {
            return;
        }

        //2.近期存在倍量
        var doubleVols = CandlePoints.TakeLastDayPoints(CandleType.DoubleVol, param.SelectDaysCount).ToArray();
        if (doubleVols.Length is 0)
        {
            return;
        }

        //3.当前价格在倍量的二一位置以上
        var currVal = CandlePoints.Last().Close;
        var midVal = (doubleVols.Last().Open + doubleVols.Last().Close) / 2.0f;
        if (currVal < midVal)
        {
            return;
        }

        //4、如果增幅超过15%，则过了最佳买点，退出 
        var rateGrow = (currVal / doubleVols.Last().Close) - 1.0f;
        if (rateGrow > 0.15f)
        {
            return;
        }

        //5、股价振荡在50%以内 
        var (isKline, klineIndex) = CandlePoints.GetTrendIndexsInLastDays(120);
        if (!isKline || klineIndex.WaveRate < 0.50f)
        {
            return;
        }

        //4、EPS在0.10以上
        var (isFina, finaIndex) = FinancialIndexs.GetLastFinaIndex();
        if (!isFina || finaIndex.Eps < 0.05f)
        {
            return;
        }

        SeleStockKeyIndicators.Add(new StockKeyIndicators
        {
            StockCode = currentStock.Ts_code,
            StockName = currentStock.Name,
            CurrentClose = currVal,
            LastEps = isFina ? finaIndex.Eps : 0,
            FloatMv = currentStock.Float_mv,
            DaysToMaxCLose = klineIndex.DaysToMax,
            GrowRate = $"{klineIndex.RatioCurrentToMax * 100.0f:F1}%",
            Industry = currentStock.Industry,
            Trend = "股价在最近倍量柱1/2位以上",
            Description = $"{CandlePoints.Last().LocationIndex - doubleVols.Last().LocationIndex}日内，进攻倍量柱，回调未破位"
        });
    }

    /// <summary>
    ///     2 底部触底振荡的股票
    /// </summary>
    /// <param name="days"></param>
    /// <param name="currentStock"></param>
    private static void SeleBottomWaveInLastDays(StockInfo currentStock, CandlePointsParam param)
    {
        //1.取拐点序列，分析价格走势,股价振荡下行           
        var (trendLable, trendDescribe)
            = CandlePoints.CreateNorderFittingPointsBySample(3).FittingPoints.AnalyTrendOfKline();
        if (!trendLable.Contains('L') || string.IsNullOrEmpty(trendDescribe))
        {
            return;
        }

        //2、股价振荡在50%以内 
        var (isKline, klineIndex) = CandlePoints.GetTrendIndexsInLastDays(param.SelectDaysCount);
        if (!isKline || klineIndex.WaveRate < 0.50f)
        {
            return;
        }

        //振荡少于8周的不考虑
        if (klineIndex.DaysToMax < 40)
        {
            return;
        }

        //3.底部振荡，底部拐点的振荡幅度在15%内。
        var (isThanThreeHitBottom, countHitBottomLessThan10P)
            = SamplePoints.AnalyWaveOnBottomPoints();
        if (!isThanThreeHitBottom)
        {
            return;
        }

        //4.近10日交易倍量出现1次以上
        if (klineIndex.DoubleVolCountsIn20Days < 1)
        {
            return;
        }

        //5.两个月40日内5日和20日金叉至少两次，最后一次金叉距离不超过一周
        var (countGoldCross, daysToCurrent)
            = CandlePoints.AnalyGoldCrossMa5toMa20InDays(40);
        if (countGoldCross < 2 || daysToCurrent > 5)
        {
            return;
        }

        //6.业绩EPS要大于0.05
        var (isFina, finaIndex)
            = FinancialIndexs.GetLastFinaIndex();
        if (!isFina || finaIndex.Eps < 0.05f)
        {
            return;
        }

        SeleStockKeyIndicators.Add(new StockKeyIndicators
        {
            StockCode = currentStock.Ts_code,
            StockName = currentStock.Name,
            CurrentClose = klineIndex.Current,
            DaysToMaxCLose = klineIndex.DaysToMax,
            LastEps = finaIndex.Eps,
            FloatMv = currentStock.Float_mv,
            GrowRate = $"股价在最高价{klineIndex.RatioCurrentToMax * 100.0f:F2}%",
            Industry = currentStock.Industry,
            Trend = $"触底10%内振荡{countHitBottomLessThan10P}次",
            Description = $"收盘价振幅:{(1 - klineIndex.WaveRate) * 100.0f:F2}%"
        });
    }

    /// <summary>
    ///     3 龙头战法，days日内涨停，且回调不破5日均线，抓再次涨停龙头
    /// </summary>
    /// <param name="days"></param>
    /// <param name="currentStock"></param>
    private static void SeleSkyrocketingtoBack(StockInfo currentStock, CandlePointsParam param)
    {
        //1、days日有内涨停
        var skyrocketings = CandlePoints.TakeLastDayPoints(CandleType.SkyrocKeting, param.SelectDaysCount);
        if (!skyrocketings.Any())
        {
            return;
        }

        var ma5Line = CandlePoints.Select(n => n.Close).CalculateMAVal(5);
        if (ma5Line.Count < 1)
        {
            return;
        }

        //2、当前价格在5日均线以上
        if (CandlePoints.Last().Close < ma5Line.Last().MAValue)
        {
            return;
        }

        //3.Eps在0.05
        var (isFina, finaIndex) = FinancialIndexs.GetLastFinaIndex();
        if (!isFina || finaIndex.Eps < 0.05f)
        {
            return;
        }

        //涨停后趋势描述
        var trendDescription = "";
        var skyrocketingIndex = skyrocketings.Last().LocationIndex;
        if (skyrocketingIndex != CandlePoints.Last().LocationIndex)
        {
            var skyrocketedTrend = (
                VolTrend: CandlePoints[skyrocketingIndex + 1].Vol > CandlePoints[skyrocketingIndex].Vol,
                PriceTrend: CandlePoints[skyrocketingIndex + 1].Close >= CandlePoints[skyrocketingIndex].Close,
                NextDayUpWard: CandlePoints[skyrocketingIndex + 1].IsCloseUpOpen);
            trendDescription = skyrocketedTrend switch
            {
                (true, true, true) => "涨停后放量冲高//次日价格冲高...",
                (true, true, false) => "涨停后放量冲高//次日价格回落...",
                (true, false, true or false) => "涨停后放量回调清洗...",
                (false, true, true) => "涨停后缩量拉升//次日价格冲高...",
                (false, true, false) => "涨停后缩量拉升//次日价格回落...",
                (false, false, true or false) => "涨停后缩量回调..."
            };
        }
        else
        {
            trendDescription = "当日冲高涨停..."; //当日涨停
        }

        SeleStockKeyIndicators.Add(new StockKeyIndicators
        {
            StockCode = currentStock.Ts_code,
            StockName = currentStock.Name,
            CurrentClose = CandlePoints.Last().Close,
            LastEps = isFina ? finaIndex.Eps : 0,
            FloatMv = currentStock.Float_mv,
            DaysToMaxCLose = CandlePoints.Last().LocationIndex - skyrocketingIndex,
            GrowRate = $"{(CandlePoints.Last().Close / skyrocketings.Last().Close) - 1.0f:P1}",
            Industry = currentStock.Industry,
            Trend = trendDescription,
            Description = $"{CandlePoints.Last().LocationIndex - skyrocketingIndex}日内,涨停进攻,回调未破位5日均线"
        });
    }

    /// <summary>
    ///     4 涨停后回调在70-85%之间的振荡，可考虑择机介入
    /// </summary>
    /// <param name="currentStock"></param>
    private static void SeleSkyrocketingtoBack30P(StockInfo currentStock, CandlePointsParam param)
    {
        //1.最近的高位拐点存在涨停
        var (isTop, lastTop) = SamplePoints.GetLastTopPoint();
        if (!isTop || !lastTop.IsSkyRocketing)
        {
            return;
        }

        //2、当日股价处于回调状态，位置为70-85%之间，基本调整到位
        if (lastTop.GrowToCurrent is < 0.68f or > 0.85f)
        {
            return;
        }

        //3.Eps在0.05
        var (isFina, finaIndex) = FinancialIndexs.GetLastFinaIndex();
        if (!isFina || finaIndex.Eps < 0.05f)
        {
            return;
        }

        SeleStockKeyIndicators.Add(new StockKeyIndicators
        {
            StockCode = currentStock.Ts_code,
            StockName = currentStock.Name,
            CurrentClose = CandlePoints.Last().Close,
            LastEps = isFina ? finaIndex.Eps : 0,
            FloatMv = currentStock.Float_mv,
            DaysToMaxCLose = lastTop.DaysToCurrent,
            GrowRate = $"{lastTop.GrowToCurrent:P1}",
            Industry = currentStock.Industry,
            Trend = "涨停回调",
            Description = $"{lastTop.DaysToCurrent}日内,涨停回调处于高点的70%-85%振荡"
        });
    }

    /// <summary>
    ///     5 尾盘30分钟，异动选股策略
    /// </summary>
    /// <param name="currentStock"></param>
    private static void SeleChangeOn30MinutesOfLateDay(StockInfo currentStock, CandlePointsParam param)
    {
        //涨幅在3%-5%之间
        var pctPriceRaise = CandlePoints.Last().Pctchg;
        if (pctPriceRaise is < 3.0f or > 5.0f)
        {
            return;
        }

        //量比大于1；
        var ratioQR = CandlePoints.GetQuantityRelativerRatioOnCurrent();
        if (ratioQR <= 1.0)
        {
            return;
        }

        //换手率在5%到10%之间
        var rateTurnOver = CandlePoints.GetTurnOverRateOnCurrent(currentStock);
        if (rateTurnOver is < 5.0f or >= 10.0f)
        {
            return;
        }

        //流通市值在20亿-200亿之间
        if (currentStock.Float_mv is < 20.0f or >= 200.0f)
        {
            return;
        }

        var (isFina, finaIndex) = FinancialIndexs.GetLastFinaIndex();
        if (!isFina || finaIndex.Eps < 0.01f)
        {
            return;
        }

        SeleStockKeyIndicators.Add(new StockKeyIndicators
        {
            StockCode = currentStock.Ts_code,
            StockName = currentStock.Name,
            CurrentClose = CandlePoints.Last().Close,
            LastEps = isFina ? finaIndex.Eps : 0,
            FloatMv = currentStock.Float_mv,
            //DaysToMaxCLose =0,
            //GrowRate = 0,
            Industry = currentStock.Industry,
            Trend = "尾盘异动",
            Description = $"价格涨幅:{pctPriceRaise:F1},量比:{ratioQR:F1},换手率:{rateTurnOver:F1}"
        });
    }

    /// <summary>
    ///     早盘抓涨停股，涨幅3-5%个点；换手率：大于3%；量比大于1.5倍；流通市值100亿元以内；没有亏损
    /// </summary>
    /// <param name="Description"></param>
    /// <param name="DoworkAction"></param>
    /// <param name="Args"></param>
    /// <returns></returns>
    private static void SeleSkyrocketingOnEarlyMarket(StockInfo currentStock, CandlePointsParam param)
    {
        //涨幅在3-5%
        var pctPriceRaise = CandlePoints.Last().Pctchg;
        if (pctPriceRaise is < 3.0f or > 5.0f)
        {
            return;
        }

        //量比大于1.5；
        var ratioQR = CandlePoints.GetQuantityRelativerRatioOnCurrent();
        if (ratioQR < 1.5)
        {
            return;
        }

        //换手率大于3
        var rateTurnOver = CandlePoints.GetTurnOverRateOnCurrent(currentStock);
        if (rateTurnOver is < 3.0f)
        {
            return;
        }

        //流通市值100亿元内        
        if (currentStock.Float_mv is > 100.0f)
        {
            return;
        }

        //没有亏损
        var (isFina, finaIndex) = FinancialIndexs.GetLastFinaIndex();
        if (!isFina || finaIndex.Eps < 0.01f)
        {
            return;
        }

        SeleStockKeyIndicators.Add(new StockKeyIndicators
        {
            StockCode = currentStock.Ts_code,
            StockName = currentStock.Name,
            CurrentClose = CandlePoints.Last().Close,
            LastEps = isFina ? finaIndex.Eps : 0,
            FloatMv = currentStock.Float_mv,
            //DaysToMaxCLose =0,
            //GrowRate = 0,
            Industry = currentStock.Industry,
            Trend = "早盘异动",
            Description = $"价格涨幅:{pctPriceRaise:F1},量比:{ratioQR:F1},换手率:{rateTurnOver:F1}"
        });
    }

    /// <summary>
    /// 持续下降24个月，月线出现5日金叉10日线
    /// </summary>
    /// <param name="currentStock"></param>
    /// <param name="param"></param>

    private static void SeleSteadyDecreaseIn24Months(StockInfo currentStock, CandlePointsParam param)
    {
        //1.选择趋势向下
        var (trendLable, trendDescribe)
            = CandlePoints.CreateNorderFittingPointsBySample(3).FittingPoints.AnalyTrendOfKline();
        if (!trendLable.Contains("L \\") || string.IsNullOrEmpty(trendDescribe))
        {
            return;
        }

        //2.量能不断萎缩

        var volArray = CandlePoints.Select(n => n.Vol).ToArray();
        var idxsOfSect = volArray.Length / 5;
        var volSectMean = (
            volArray.Take(idxsOfSect).Sum() / idxsOfSect,
            volArray.Skip((volArray.Length - idxsOfSect) / 2).Take(idxsOfSect).Sum() / idxsOfSect,
            volArray.TakeLast(idxsOfSect).Sum() / idxsOfSect);

        if (volSectMean.Item2 > volSectMean.Item1 ||
        volSectMean.Item3 > volSectMean.Item2)
        {
            return;
        }
        //3.3个月内5日和10日金叉至少1次
        var (countGoldCross, daysToCurrent)
            = CandlePoints.AnalyGoldCrossMa5toMa10InDays(3);
        if (countGoldCross < 1 || countGoldCross > 3)
        {
            return;
        }
        //计算价格下降幅度
        var (maxClose, minClose) = CandlePoints.MaxMinFloat(n => n.Close);
        var pctPriceRaise = (maxClose - minClose) / maxClose;
        //取财务指标
        var (isFina, finaIndex) = FinancialIndexs.GetLastFinaIndex();
        SeleStockKeyIndicators.Add(new StockKeyIndicators
        {
            StockCode = currentStock.Ts_code,
            StockName = currentStock.Name,
            CurrentClose = CandlePoints.Last().Close,
            LastEps = isFina ? finaIndex.Eps : 0,
            FloatMv = currentStock.Float_mv,
            //DaysToMaxCLose =0,
            //GrowRate = 0,
            Industry = currentStock.Industry,
            Trend = $"{param.SelectMonthsCount}月内价格持续下滑/月线5-10金叉",
            Description = $"下降幅度:{pctPriceRaise:P1}"
        });
    }
}

public record DoworkOfTactics
(
    string Description,
    Action<StockInfo, CandlePointsParam> DoWork_Action,
    CandlePointsParam Param
);
public record CandlePointsParam
(
    CandlePointSet.PeriodType PeriodType,
    int SelectMonthsCount,
    int SelectDaysCount,
    bool IsFillCurrentDailyOnline
);

public class StockKeyIndicators
{
    public string StockCode { get; set; }
    public string StockName { get; set; }
    public bool IsSelect { get; set; }
    public string Industry { get; set; }
    public float CurrentClose { get; set; }
    public float LastEps { get; set; }
    public float FloatMv { get; set; }
    public int DaysToMaxCLose { get; set; }
    public string Trend { get; set; }
    public string GrowRate { get; set; }
    public string Description { get; set; }
}